--[[
Name: ArenaTrack-0.1
Revision: $Rev: 4 $
Author(s): Iakona @ Bonechewer-US 
           Jyke   @ Bonechewer-US
Inspired By: MagicTrack by Dw 
Website: http://code.google.com/p/arenatrack/
Documentation: http://code.google.com/p/arenatrack/wiki/
SVN: https://arenatrack.googlecode.com/svn/trunk/
Description: Tracks and communicates arena events
License: New BSD License

Copyright (c) 2008, Misanthopos

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions 
and the following disclaimer. 

Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
and the following disclaimer in the documentation and/or other materials provided with the 
distribution. 

Neither the name of the Misanthropos nor the names of its contributors may be used to endorse or 
promote products derived from this software without specific prior written permission. 

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

]]

ArenaTrack = AceLibrary("AceAddon-2.0"):new("AceComm-2.0","AceConsole-2.0","AceEvent-2.0","AceDB-2.0")

ArenaTrack.commPrefix = "ArenaTrack"

local paint = AceLibrary:HasInstance("PaintChips-2.0") and AceLibrary("PaintChips-2.0") or nil

-- command line options
local options = { 
   type='group',
   args = {
      trackInterupts = {
		type = 'toggle',
		name = 'Track Interupts',
		desc = 'Toggles interupt tracking',
		get = "IsInteruptsTracked",
		set = "ToggleInteruptTracking", 
      },
	  trackShields = {
		type = 'toggle',
		name = 'Track Shields',
		desc = 'Toggles shield tracking',
		get = "IsShieldsTracked",
		set = "ToggleShieldTracking", 
	  },
	  scale = {
		type = 'range',
		name = 'Scale',
		desc = 'Sets the scale',
		min = 0,
		max = 5, -- not sure the max scale
		step = 1,
		get = function()
			return ArenaTrack.db.profile.scale;
		end,
		set = function(v)
			ArenaTrack.db.profile.scale = v;
  			ArenaTrack.spellFrame:SetScale(v);
  			ArenaTrack.buffFrame:SetScale(v);
		end,
	  },
	  lock = {
		type = 'toggle',
		name = 'Lock Frames',
		desc = 'Toggles frame locking',
		get = "IsFramesLocked",
		set = "ToggleFrameLocking", 
	  },
	  showAnchor = {
	  	type = 'toggle',
		name = 'Lock Frames',
		desc = 'Toggles frame locking',
		get = "ArenaTrack_ShowAnchors",
		set = "ArenaTrack_HideAnchors",
	  },
   },
}

ArenaTrack:RegisterChatCommand("/arenatrack", "/at", options)
-- ArenaTrack DB
ArenaTrack:RegisterDB("ArenaTrackDB","ArenaTrackDBPC")
ArenaTrack:RegisterDefaults("profile", {
    trackInterrupts = true, 
    trackShields = true, 
    scale = 1,
    msgscale = 5,
    locked = nil,
    posx = nil,
    posy = nil,
    buffs = { 
	["Divine Shield"] = 
	{
		duration = 12,
		icon = "Interface\\Icons\\Spell_Holy_DivineIntervention",
		type = 1,
	},
	["Ice Block"] = 
	{
		duration = 10,
		icon = "Interface\\Icons\\Spell_Frost_Frost",
		type = 1,
	},
	["Blessing of Protection"] = 
	{
		duration = 10,
		icon = "Interface\\Icons\\Spell_Holy_SealOfProtection",
		type = 1,
	},
	["Blessing of Freedom"] = 
	{
		duration = 16,
		icon = "Interface\\Icons\\Spell_Holy_SealOfValor",
		type = 1,
	},
	["Blessing of Sacrifice"] = 
	{
		duration = 30,
		icon = "Interface\\Icons\\Spell_Holy_SealOfSacrifice",
		type = 1,
	},
	["Cloak of Shadows"] =
	{
		duration = 5,
		icon = "Interface\\Icons\\Spell_Shadow_NetherCloak",
		type = 1,
	},
	["Berserker Rage"] = 
	{
		duration = 10,
		icon = "Interface\\Icons\\Spell_Nature_AncestralGuardian",
		type = 0,
	},--[[
	["Death Wish"] = 
	{
		duration = 30,
		icon = "Interface\\Icons\\Spell_Shadow_DeathPact",
		type = 1,
	},]]
	["Whirlwind"] = 
	{
		duration = 12,
		icon = "Interface\\Icons\\Ability_Whirlwind",
		type = 0,
	},
	["Spell Reflection"] =
	{
		duration = 5,
		icon = "Interface\\Icons\\Ability_Warrior_ShieldReflection",
		type = 0,
	},
   },
   spells = { 
	["Spell Lock"] = 
	{
		cooldown = 24,
		icon = "Interface\\Icons\\Spell_Shadow_MindRot",
		type = 1,
	},
	["Counterspell - Silenced"] = 
	{
		cooldown = 24,
		icon = "Interface\\Icons\\Spell_Frost_IceShock",
		type = 1,
	},
   },
   events = {
  	"CHAT_MSG_SPELL_SELF_DAMAGE",--your interrupts
	"CHAT_MSG_SPELL_BREAK_AURA",--ability removed purge
      "CHAT_MSG_SPELL_SELF_BUFF",--dispell fail
   },
   interruptEvents = {
  	"CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE",--afflicted by (counterspell)
	"CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE",--afflicted by (counterspell)
      "CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE",--resisted (counterspell)
   },
   shieldEvents = {
 	 "CHAT_MSG_SPELL_AURA_GONE_OTHER",--buff fade enemny
	 "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS",--buff gain enemy
	 "CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS",
   }, 
} )

local anchor = nil

-- lets get this party started
function ArenaTrack:OnInitialize()
  self:chatPrint("Loaded");
  
  self.frame = self:createMsgFrame();

  -- You interupt %s's %s
  self.selfInterruptOther = self:formatgs(SPELLINTERRUPTSELFOTHER)
  -- You are afflicted by %s (Combat text for aura events)
  self.selfGetAfflicted = self:formatgs(AURAADDEDSELFHARMFUL)
  -- %s is afflicted by %s
  self.friendlyGetAfflicted = self:formatgs(AURAADDEDOTHERHARMFUL)
  -- %s's %s was resisted by %s
  self.friendlyResistSpell = self:formatgs(SPELLRESISTOTHEROTHER)
  -- %s's %s was resisted
  self.selfResistSpell = self:formatgs(SPELLRESISTOTHERSELF)
  -- You fail to dispell %s's %s
  self.selfFailDispel = self:formatgs(DISPELFAILEDSELFOTHER)
  -- %s gains %s (Combat text for aura events)
  self.enemyGainBuff = self:formatgs(AURAADDEDOTHERHELPFUL)
  -- %s fades from %s
  self.enemyLoseBuff = self:formatgs(AURAREMOVEDOTHER)

  self.UpdateInterval = 0.1;
  self.TimeSinceLastUpdate = 0;
  self.spellsWatching = 0;
  self.buffsWatching = 0;

  --self.buffFrame = self:setupBaseFrame("ArenaTrackBuffs");
  --self.spellFrame = self:setupBaseFrame("ArenaTrackSpells");

  self:regTable(self.db.profile.events);
  -- I like it when they call me big papa
  self.playerName = UnitName("player");

  if ( self.db.profile.trackInterrupts ~= nil ) then
	self:regTable(self.db.profile.interruptEvents);
  end
  if ( self.db.profile.trackShields ~= nil ) then
	self:regTable(self.db.profile.shieldEvents);
  end
end

function ArenaTrack:OnEnable()
	self:RegisterEvent("ArenaTrack_Message")
	self:RegisterEvent("ArenaTrack_ShowAnchors")
	self:RegisterEvent("ArenaTrack_HideAnchors")
	self:RegisterEvent("ArenaTrack_Test")

    self.frame:SetScript("OnEvent", function() self:onEvent() end);
    -- setting up the comm chanel
    self:SetCommPrefix(self.commPrefix);
    -- probably should set GROUP to PARTY
    self:RegisterComm(self.commPrefix, "GROUP", "ReceiveCommMessage");
    self:SetDefaultCommPriority("ALERT");
end

function ArenaTrack:OnDisable()
    self:ArenaTrack_HideAnchors()
end

-- converts escaped characters in a string to a matchable pattern
-- example: "You interrupt %s's %s" converts to "You interrupt (.+)'s (.+)"
function ArenaTrack:formatgs(gstring, anchor)
	gstring = string.gsub(gstring,"([%^%(%)%.%[%]%*%+%-%?])","%%%1")
	gstring = string.gsub(gstring,"%%s","(.+)")
	gstring = string.gsub(gstring,"%%d","(%-?%%d+)")
	if anchor then gstring = "^"..gstring end
	return gstring
end

-- simple print to chat function
function ArenaTrack:chatPrint(msg)
  DEFAULT_CHAT_FRAME:AddMessage("Addon_ArenaTrack: "..msg);
end

-- Setting up the locals
local selfInterruptOther
local selfGetAfflicted
local friendlyGetAfflicted
local friendlyResistSpell
local selfResistSpell
local selfFailDispel
local enemyGainBuff
local enemyLoseBuff
local frame;
local buffFrame;
local spellFrame;
local onUpdate;
local playerName;
local UpdateInterval;
local TimeSinceLastUpdate;
local spellsWatching;
local buffsWatching;

-- returns true if unit is in party, otherwise nil
function ArenaTrack:inparty(unit)
	for i = 1, GetNumPartyMembers() do
		local name = UnitName("party"..i)
		if name == unit then
			return true
		end
	end
end

-- register table of events
function ArenaTrack:regTable(t)
   for _,v in pairs(t) do
	self.frame:RegisterEvent(v);
   end
end

-- unreguster tabkes
function ArenaTrack:unregTable(t)
   for _,v in pairs(t) do
	self.frame:UnregisterEvent(v);
   end
end

-- set but the time tracking frame
function ArenaTrack:setupBaseFrame(n)
  local f;
  f = CreateFrame("Frame", n, UIParent);
  if not f:IsUserPlaced() then
	f:SetPoint("CENTER")
  end
  f:SetWidth(20);
  f:SetHeight(20);
  f:SetMovable(1);
  f.active = {};
  f:RegisterForDrag("LeftButton");
  f:SetScript("OnDragStart", function() this:StartMoving(); end);
  f:SetScript("OnDragStop", function() this:StopMovingOrSizing(); end);
  f.bg = f:CreateTexture(n.."Bg", "BACKGROUND");
  f.bg:SetWidth(20);
  f.bg:SetHeight(20);
  f.bg:SetVertexColor(0.50, 0.50, 0.50, 1);
  f.bg:SetTexture("Interface\\TargetingFrame\\UI-StatusBar");
  f.bg:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 0, 0);
  if ( self.db.profile.locked ) then
	f.bg:Hide();
  else
	f:EnableMouse(1);
  end
  f:SetScale(self.db.profile.scale)
  return f;
end

-- unlocks time tracking frames
function ArenaTrack:unlockFrame(f)
  f.bg:Show()
  f:EnableMouse(1);
end

-- locks time tracking frames
function ArenaTrack:lockFrame(f)
  f.bg:Hide()
  f:EnableMouse(0);
end

-------------------------------------
--- Start Command Functions
-------------------------------------
-- are the time tracking frames locked?
function ArenaTrack:IsFramesLocked()
	return self.db.profile.locked;
end

-- toggle time tracking frame locking
function ArenaTrack:ToggleFrameLocking()
  if self.db.profile.locked then
	ArenaTrackDB.locked = nil;
	self:unlockFrame(self.buffFrame);
	self:unlockFrame(self.spellFrame);
	self:chatPrint("unlocked.");
  else
	self.db.profile.locked = true;
	self:lockFrame(self.buffFrame);
	self:lockFrame(self.spellFrame);
	self:chatPrint("locked.");
  end
end

-- are we tracking interupt events?
function ArenaTrack:IsInteruptsTracked()
	return self.db.profile.trackInterrupts;
end

-- toggles interupt tracking
function ArenaTrack:ToggleInteruptTracking()
  if self.db.profile.trackInterrupts then
	self.db.profile.trackInterrupts = nil;
	self:chatPrint("no interrupts");
	self:unregTable(self.db.profile.interruptEvents);
  else
	self.db.profile.trackInterrupts = true;
	self:chatPrint("interrupts");
	self:regTable(self.db.profile.interruptEvents);
  end
end

-- are we tracking shields?
function ArenaTrack:IsShieldsTracked()
	return self.db.profile.trackShields;
end

-- toggle shield tracking
function ArenaTrack:ToggleShieldTracking()
  if self.db.profile.trackShields then
	self.db.profile.trackShields = nil;
	self:chatPrint("no shields");
	self:unregTable(self.db.profile.shieldEvents);
  else
	self.db.profile.trackShields = true;
	self:chatPrint("shields");
	self:regTable(self.db.profile.shieldEvents);
  end
end

-------------------------------------
--- End Command Functions
-------------------------------------
-- main message function; fires ArenaTrack_Message event
function ArenaTrack:Message(...)
	self:TriggerEvent("ArenaTrack_Message", ...)
end

-- anchor test button event
function ArenaTrack:ArenaTrack_Test() 
	-- DO NOT CHANGE
	local inParty = false;

	if (GetNumPartyMembers() > 0) then
		inParty = true;
  	end

	if( inParty ) then
		self:SendCommMessage("GROUP", self.playerName, "target", "spell", "DEBUFF")
		self:SendCommMessage("GROUP", "test_party", "target", "spell", "DEBUFF")
		self:SendCommMessage("GROUP", "caster", "target", "spell", "DEBUFF") 
	else
		self:SendSelfMessage(self.playerName, "target", "spell", "DEBUFF")
		self:SendSelfMessage("test_party", "target", "spell", "DEBUFF")
		self:SendSelfMessage("caster", "target", "spell", "DEBUFF")
	end
	-- END DO NOT CHANGE
	
	-- START CUSTOM TESTING CODE
	
	-- END CUSTOM TESTING CODE
end

-- updates the time tracker frame text
function ArenaTrack:updateTextFrame(f,text)
  f:SetText(string.format("%.1f",text))
end

-- removes time tracking frame
function ArenaTrack:removeFrame(f)
  f:Hide();
  f.inUse = nil;
end

-- create a new time tracking frame
function ArenaTrack:createFrame(name)
  local tempFrame, tempText, background;
  tempFrame = CreateFrame("Frame", name, UIParent);
  tempFrame:SetWidth(50);
  tempFrame:SetHeight(20);
  local background = tempFrame:CreateTexture(name.."Bg", "BACKGROUND");
  background:SetWidth(20);
  background:SetHeight(20);
  background:SetPoint("LEFT");
  tempText = tempFrame:CreateFontString(name.."Text", "OVERLAY");
  tempText:SetFontObject("GameFontNormal");
  tempText:SetTextColor(1,1,1,1);
  tempText:SetPoint("LEFT",20,0);
  tempFrame.text = tempText
  tempFrame.texture = background
  tempFrame:SetScale(self.db.profile.scale)
  return tempFrame;
end

-- adds spell to the time tracking spell queue
function ArenaTrack:addSpell(spellName, spellInfo)
  if spellInfo.type == 1 then
	self:messagePrint(spellName.." Used");
	if ( self.spellFrame.active[spellName] == nil ) then
		self.spellFrame.active[spellName] = self:createFrame("ArenaTrackSpell"..spellName);
		self.spellFrame.active[spellName].texture:SetTexture(self.db.profile.spells[spellName].icon);
	end
	if ( self.spellFrame.active[spellName].inUse == nil ) then
		self.spellFrame.active[spellName]:Show();
		self.spellFrame.active[spellName]:SetPoint("BOTTOMLEFT", self.spellFrame, "BOTTOMLEFT", 0, 20*self.spellsWatching);
		self.spellFrame.active[spellName].inUse = 1;
		self.frame:SetScript("OnUpdate", function() self:onUpdate() end);
		self.db.profile.spellsWatching = self.spellsWatching + 1;
	end
	self:messagePrint(self.spellFrame.active[spellName].text);
	self.updateTextFrame(self.spellFrame.active[spellName].text,self.db.profile.spells[spellName].cooldown);
	self.spellFrame.active[spellName].cooldown = self.db.profile.spells[spellName].cooldown
  end
end

-- adds buff to the time tracking buff queue
function ArenaTrack:addBuff(buffName, buffInfo, target)
  if buffInfo.type == 1 then
	self.messagePrint(buffName.." GAIN "..target);
	if ( self.buffFrame.active[buffName] == nil ) then
		self.buffFrame.active[buffName] = self:createFrame("ArenaTrackBuff"..buffName);
		self.buffFrame.active[buffName].texture:SetTexture(self.db.profile.spells[buffName].icon);
	end
	if ( self.buffFrame.active[buffName].inUse == nil ) then
		self.buffFrame.active[buffName]:Show();
		self.buffFrame.active[buffName]:SetPoint("BOTTOM", self.buffFrame, "BOTTOM", 0, 20*self.buffsWatching);
		self.buffFrame.active[buffName].inUse = 1;
		self.frame:SetScript("OnUpdate", function() self:onUpdate() end);
		self.db.profile.buffsWatching = self.buffsWatching + 1;
	end
	self.buffFrame.active[buffName].duration = self.db.profile.spells[buffName].duration
	self.updateTextFrame(buffFrame.active[buffName].text,self.db.profile.spells[buffName].duration);
	self.buffFrame.active[buffName].target = target
  end
end

-- remove buff frame and entry from the time tracking buff queue
function ArenaTrack:removeBuff(buffName, buffInfo, target)
  if buffInfo.type == 1 then
	if buffFrame.active[buffName] ~= nil and buffFrame.active[buffName].target == target then
		self:messagePrint(buffName.." FADE "..self.buffFrame.active[buffName].target.." GOGO");
		self:removeFrame(self.buffFrame.active[buffName])
		self.db.profile.buffsWatching = self.db.profile.buffsWatching - 1;
		if self.db.profile.buffsWatching == 0 and self.db.profile.spellsWatching == 0 then
			self.frame:SetScript("OnUpdate", nil);
		end
	end
  end
end

-- main on update function this event it registered on frames that we are activating 
-- add or removes frames depending on the time start or end of a spell/buff/interupt event
function ArenaTrack:onUpdate()
  self.TimeSinceLastUpdate = self.TimeSinceLastUpdate + arg1;

  if (self.TimeSinceLastUpdate > self.UpdateInterval) then
	if self.buffsWatching > 0 then
		for k,v in pairs(self.buffFrame.active) do
			if ( v.inUse ~= nil ) then
				v.duration = v.duration - self.TimeSinceLastUpdate;
				if v.duration <= 0 then
					self:removeBuff(k,self.db.profile.buffs[k],v.target);
				else
					self:updateTextFrame(v,v.duration);
					--self:updateTextFrame(v.text,v.duration);
				end
			end
		end
	end
	
	if self.spellsWatching > 0 then
		for k,v in pairs(self.spellFrame.active) do
			if ( v.inUse ~= nil ) then
				v.cooldown = v.cooldown - self.TimeSinceLastUpdate;
				if v.cooldown <= 0 then
					self:messagePrint(k.." UP WATCH OUT");
					self:removeFrame(v)
					self.spellsWatching = self.spellsWatching - 1;
					if self.buffsWatching == 0 and self.spellsWatching == 0 then
						self.frame:SetScript("OnUpdate", nil);
					end
				else
					--self:updateTextFrame(v.text,v.cooldown);
					self:updateTextFrame(v,v.cooldown);
				end
			end
		end
	end
	self.TimeSinceLastUpdate = 0;
  end
end

-- process spell cast event; is it one we are watching?
function ArenaTrack:processSpellCast(spell)
	for k,v in pairs(self.db.profile.spells) do
		if k == spell then
			self:addSpell(k,v)
		end
	end
end

-- process buff cast event; is it one we are watching?
function ArenaTrack:processBuffGain(buff,target)
	for k,v in pairs(self.db.profile.buffs) do
		if k == buff then
			self:addBuff(k,v,target)
		end
	end
end

-- process the buff fade event; call removeBuff function
function ArenaTrack:processBuffFade(buff,target)
		for k,v in pairs(self.buffFrame.active) do
			if v.inUse ~= nil and k == buff then
				self:removeBuff(k,self.db.profile.buffs[k],target)
			end
		end
end

-------------------------------------
--- Start Messaging Functions
-------------------------------------
-- creates main messaging frame
function ArenaTrack:createMsgFrame()
	local messageFrame

	messageFrame = CreateFrame("MessageFrame", "ArenaTrackMsgFrame", UIParent)
	messageFrame:SetWidth(512)
	messageFrame:SetHeight(80)

	if not anchor then self:SetupFrames() end
	
	messageFrame:SetPoint("TOP", anchor, "BOTTOM", 0, 0);
	messageFrame:SetScale(self.db.profile.msgscale or 1);
	messageFrame:SetInsertMode("TOP");
	messageFrame:SetFrameStrata("HIGH");
	messageFrame:SetToplevel(true);
	messageFrame:SetFontObject("GameFontNormalLarge");
	messageFrame:Show();

	return messageFrame;
end

--[[ Event handlers ]]
-- main print function
function ArenaTrack:Print(addon, text, r, g, b)
	if not self.frame then createMsgFrame() end
	self.frame:SetScale(self.db.profile.msgscale)
	self.frame:AddMessage(text, r, g, b, 1, UIERRORS_HOLD_TIME)
end

-- message processing function (event it registered in OnEnable)
function ArenaTrack:ArenaTrack_Message(text, color, noraidsay, sound, broadcastonly)
	if broadcastonly or not text then return end
	
	local r, g, b = 1, 1, 1 -- Default to white.
	-- want red for enemies, green for party members, and white for self	
	if ( color == "green" ) then
		r, g, b = 0, 1, 0
	elseif ( color == "red" ) then
		r, g, b = 1, 0, 0
	end

	self:Print("ArenaTrack",text,r,g,b);
end

--[[ anchor functions ]]
--  show anchor
function ArenaTrack:ArenaTrack_ShowAnchors()
	if not anchor then self:SetupFrames() end
	anchor:Show()
end

-- hide anchor
function ArenaTrack:ArenaTrack_HideAnchors()
	if not anchor then return end
	anchor:Hide()
end

-- set up anchor frame
function ArenaTrack:SetupFrames()
	if anchor then return end

	anchor = CreateFrame("Frame", "ArenaTrackMessageAnchor", UIParent)
	anchor:Hide()

	anchor:SetWidth(120)
	anchor:SetHeight(80)

	anchor:SetBackdrop({
		bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
		edgeFile = "Interface\\AddOns\\ArenaTrack\\Textures\\otravi-semi-full-border", edgeSize = 32,
		insets = {left = 1, right = 1, top = 20, bottom = 1},
	})

	anchor:SetBackdropColor(24/255, 24/255, 24/255)
	anchor:ClearAllPoints()
	anchor:SetPoint("TOP", UIParent, "TOP", 0, -150)
	anchor:EnableMouse(true)
	anchor:RegisterForDrag("LeftButton")
	anchor:SetMovable(true)
	anchor:SetScript("OnDragStart", function() this:StartMoving() end)
	anchor:SetScript("OnDragStop", function()
		this:StopMovingOrSizing()
		self:SavePosition()
	end)

	local cheader = anchor:CreateFontString(nil,"OVERLAY")
	cheader:ClearAllPoints()
	cheader:SetWidth(110)
	cheader:SetHeight(15)
	cheader:SetPoint("TOP", anchor, "TOP", 0, -14)
	cheader:SetFont("Fonts\\FRIZQT__.TTF", 12)
	cheader:SetJustifyH("LEFT")
	cheader:SetText("Messages")
	cheader:SetShadowOffset(.8, -.8)
	cheader:SetShadowColor(0, 0, 0, 1)

	local close = anchor:CreateTexture(nil, "ARTWORK")
	close:SetTexture("Interface\\AddOns\\ArenaTrack\\Textures\\otravi-close")
	close:SetTexCoord(0, .625, 0, .9333)

	close:SetWidth(20)
	close:SetHeight(14)
	close:SetPoint("TOPRIGHT", anchor, "TOPRIGHT", -7, -15)

	local closebutton = CreateFrame("Button", nil)
	closebutton:SetParent( anchor )
	closebutton:SetWidth(20)
	closebutton:SetHeight(14)
	closebutton:SetPoint("CENTER", close, "CENTER")
	closebutton:SetScript( "OnClick", function() self:ArenaTrack_HideAnchors() end )

	local testbutton = CreateFrame("Button", nil, anchor, "UIPanelButtonTemplate")
	testbutton:SetWidth(60)
	testbutton:SetHeight(25)
	testbutton:SetText("Test")
	testbutton:SetPoint("CENTER", anchor, "CENTER", 0, -16)
	testbutton:SetScript( "OnClick", function()  self:TriggerEvent("ArenaTrack_Test") end )

	-- TODO: Set up option to test message frame
	--if not x then testbutton:Hide() end

	self:RestorePosition()
end

-- reset anchor position
function ArenaTrack:ResetAnchor()
	if not anchor then self:SetupFrames() end

	anchor:ClearAllPoints()
	anchor:SetPoint("TOP", UIParent, "TOP", 0, -150)
	self.db.profile.posx = nil
	self.db.profile.posy = -150
end

-- save anchor position
function ArenaTrack:SavePosition()
	if not anchor then self:SetupFrames() end

	local s = anchor:GetEffectiveScale()

	self.db.profile.posx = anchor:GetLeft() * s
	self.db.profile.posy = anchor:GetTop() * s
end

-- restore anchor frame position
function ArenaTrack:RestorePosition()
	if not anchor then self:SetupFrames() end

	local x = self.db.profile.posx
	local y = self.db.profile.posy

	if x and y then
		local s = anchor:GetEffectiveScale()
		anchor:ClearAllPoints()
		anchor:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
	else
		self:ResetAnchor()
	end
end
-------------------------------------
--- End Messaging Functions
-------------------------------------

-------------------------------------
--- Start Comm Recieve Function
-------------------------------------
--[[ Send Comm message parameters (distribution, caster, target, spell, type)
	distribution - channel hardcoded GROUP
	sender - player sending message
	caster - caster of the affecting spell
      target - affected by spell
	spell - spell name
	type - spell type ("INTERUPT", "BUFF", "ENEMY_BUFF", "DEBUFF", "SPELL", "DISPELL", "DISPELL_FAIL", "ENEMY_LOSE_BUFF")
]]
-- receive comm event handler (registered in OnEnable)
function ArenaTrack:ReceiveCommMessage(prefix, sender, distribution, caster, target, spell, type)
  	local sndStr = self:GetEventMessage(caster, target, spell, type)
  	
  	local color = nil;
  	if( (self:inparty(caster)) or (caster == "test_party") ) then
  		color = "green";
  	elseif ( caster == self.playerName ) then
  		color = "white";
  	else
  		color = "red";
  	end
  	self:Message(sndStr, color);
end
-------------------------------------
--- End Comm Recieve Function
-------------------------------------
-- non-party function - by passes comm messaging
function ArenaTrack:SendSelfMessage(caster, target, spell, type)
  	local sndStr = self:GetEventMessage(caster, target, spell, type)
  	local color = nil;
  	
  	if( self:inparty(caster) or caster == "test_party" ) then
  		color = "green";
  	elseif ( caster == self.playerName ) then
  		color = "white";
  	else
  		color = "red";
  	end
  	self:Message(sndStr,color);
end

-- returns event string
function ArenaTrack:GetEventMessage(caster, target, spell, type) 
	local retStr = nil
	
	if( type == "INTERUPT") then
  		-- current interupts are only reported by party members meaning they are bad for party
  	  retStr = target.." is INTERUPTED";
  	elseif (type == "BUFF" ) then 
  		-- no messages for buff at this point
  	elseif (type == "ENEMY_BUFF") then
  		retStr = target.." gains "..spell;
  	elseif (type == "DEBUFF") then
  		retStr = target.." is afflicted with "..spell.." by "..caster; 
  	elseif (type == "SPELL") then
	  	-- currently no messages for this value
  	elseif (type == "DISPELL") then
	  	retStr = caster.." dispels ".." "..target.."'s "..spell;
  	elseif (type == "DISPELL_FAIL") then 
	  	retStr = caster.." fails to dispell "..target;
  	elseif (type == "ENEMY_LOSE_BUFF") then
	  	retStr = spell.." fades from "..target;
  	end
  	
  	return retStr;
end

-- the heart of this bitch ... processes events (registered on the MessageFrame in OnEnable)
function ArenaTrack:onEvent()
  local target,spell,caster, inParty = false;
  
  if (GetNumPartyMembers() > 0) then
	inParty = true;
  end

  if ( event == "VARIABLES_LOADED") then
	self:onLoad();
  elseif ( event == "CHAT_MSG_SPELL_SELF_DAMAGE" ) then
  	for target,spell in string.gmatch(arg1, self.selfInterruptOther) do
		-- >> Interrupt <<
		if( inParty ) then
		    self:SendCommMessage("GROUP", caster, target, spell, "INTERUPT") 
		else
			self:SendSelfMessage(caster, target, spell, "INTERUPT")
		end
	end
  elseif ( event == "CHAT_MSG_SPELL_SELF_BUFF" ) then
  	for target in string.gmatch(arg1, self.selfFailDispel) do
		-- << DISPELL FAIL >>
		if( inParty ) then
		    self:SendCommMessage("GROUP", caster, target, spell, "DISPELL_FAIL") 
		else
			self:SendSelfMessage(caster, target, spell, "DISPELL_FAIL")
		end
	end
  elseif ( event == "CHAT_MSG_SPELL_BREAK_AURA" ) then
  	for target,spell,caster in string.gmatch(arg1, "([^%s]+) (.+) is removed by (.+)'s .+") do
		if ( caster == self.playerName ) then
			-- You purge .. target .. spell
			self:SendSelfMessage(caster, target, spell, "DISPELL")
		end
	end
  elseif ( event == "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS" or event == "CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS" ) then
  	for target,spell in string.gmatch(arg1, self.enemyGainBuff) do
		if( inParty ) then
		    self:SendCommMessage("GROUP", caster, target, spell, "ENEMY_BUFF") 
		else
			self:SendSelfMessage(caster, target, spell, "ENEMY_BUFF")
		end
 		--self:processBuffGain(spell,target);
	end
  elseif ( event == "CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE" ) then
  	for target,spell in string.gmatch(arg1, self.selfResistSpell) do
			--self:processSpellCast(spell)
	end
  	for caster,spell,target in string.gmatch(arg1, self.friendlyResistSpell) do
			--self:processSpellCast(spell)
	end
  elseif ( event == "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE" ) then
  	for spell in string.gmatch(arg1, self.selfGetAfflicted) do
		if( inParty ) then
		    self:SendCommMessage("GROUP", caster, target, spell, "DEBUFF") 
		else
			self:SendSelfMessage(caster, target, spell, "DEBUFF")
		end
		--self:processSpellCast(spell)
	end
  elseif ( event == "CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE" ) then
  	for target,spell in string.gmatch(arg1, self.friendlyGetAfflicted) do
		if( inParty ) then
		    self:SendCommMessage("GROUP", caster, target, spell, "DEBUFF") 
		else
			self:SendSelfMessage(caster, target, spell, "DEBUFF")
		end
		--self:processSpellCast(spell)
	end
  elseif ( self.buffsWatching > 0 and event == "CHAT_MSG_SPELL_AURA_GONE_OTHER" ) then
  	for spell,target in string.gmatch(arg1, self.enemyLoseBuff) do
		if( inParty ) then
		    self:SendCommMessage("GROUP", caster, target, spell, "ENEMY_LOSE_BUFF") 
		else
			self:SendSelfMessage(caster, target, spell, "ENEMY_LOSE_BUFF")
		end
		--self:processBuffFade(spell,target)
	end
  end
end

--[[ FIN ]]